package com.ndp.fb.walle.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.ndp.ec.core.PublishService;
import com.ndp.ec.core.api.AudienceManagerService;
import com.ndp.fb.constant.CommonConstants;
import com.ndp.fb.enums.ErrorType;
import com.ndp.fb.exception.AdsetLimitException;
import com.ndp.fb.exception.BusinessException;
import com.ndp.fb.exception.CheckedException;
import com.ndp.fb.exception.ErrorCode;
import com.ndp.fb.model.DateModel;
import com.ndp.fb.model.business.bo.targetingspec.City;
import com.ndp.fb.model.business.bo.targetingspec.Country;
import com.ndp.fb.model.business.bo.targetingspec.LocalLocation;
import com.ndp.fb.model.business.bo.useraudience.result.LocalTargetingSpecs;
import com.ndp.fb.model.business.bo.useraudience.result.TargetingSpecGroupBo;
import com.ndp.fb.model.business.vo.advertising.param.AdImageVoParam;
import com.ndp.fb.model.business.vo.advertising.param.AdVideoVoParam;
import com.ndp.fb.model.business.vo.advertising.param.BidInfoVoParam;
import com.ndp.fb.model.business.vo.cache.param.Schedule;
import com.ndp.fb.model.business.vo.cache.result.BidCache;
import com.ndp.fb.model.business.vo.cache.result.ChooseType;
import com.ndp.fb.model.business.vo.cache.result.EntrustAccountVo;
import com.ndp.fb.model.business.vo.cache.result.GeoSubPublishBatchCache;
import com.ndp.fb.model.business.vo.cache.result.NameAssembly;
import com.ndp.fb.model.business.vo.cache.result.PageVo;
import com.ndp.fb.model.business.vo.cache.result.PublishCache;
import com.ndp.fb.model.business.vo.cache.result.UserAudienceCache;
import com.ndp.fb.model.business.vo.publish.param.EcPublishBatchVoParam;
import com.ndp.fb.rdb.model.Page;
import com.ndp.fb.rdb.model.Pager;
import com.ndp.fb.rdb.model.PublishBatch;
import com.ndp.fb.rdb.model.User;
import com.ndp.fb.redis.api.RedisClient;
import com.ndp.fb.redis.model.AdPublishCache;
import com.ndp.fb.util.DateFormatUtil;
import com.ndp.fb.util.ListUtil;
import com.ndp.fb.util.StringUtil;
import com.ndp.fb.walle.business.AdCampNameCustomService;
import com.ndp.fb.walle.business.CacheService;
import com.ndp.fb.walle.business.EntrustAccountService;
import com.ndp.fb.walle.business.PageService;
import com.ndp.fb.walle.business.PublishBatchService;
import com.ndp.fb.walle.business.PublishCacheService;
import com.ndp.fb.walle.business.TagForBuildnameService;
import com.ndp.fb.walle.model.bo.publishbatch.result.PublishBatchDetailBo;
import com.ndp.fb.model.business.vo.BaseVoParam;
import com.ndp.fb.walle.model.vo.OutputResult;
import com.ndp.fb.walle.model.vo.PageResult;
import com.ndp.fb.walle.model.vo.cache.param.CacheBudgetBidVoParam;
import com.ndp.fb.walle.model.vo.cache.param.CacheCreativeVoParam;
import com.ndp.fb.walle.model.vo.cache.param.PublishCacheParam;
import com.ndp.fb.walle.model.vo.publishbatch.result.PublishBatchDetailInfoResult;
import com.ndp.fb.walle.model.vo.publishbatch.result.PublishBatchVoDetailResult;
import com.ndp.fb.walle.util.ConversionUtils;
import com.ndp.fb.walle.util.ResponseUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Created by Administrator on 2014/10/24.
 */
@RequestMapping("/publishbatchs")
@Controller
public class PublishBatchController {

    private final org.slf4j.Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * publishBatchService
     */
    @Autowired
    private PublishBatchService publishBatchService;

    @Autowired
    private PublishCacheService publishCacheService;

    @Autowired
    private CacheService cacheService;

    @Autowired
    private RedisClient redisClient;

    @Reference
    private PublishService publishService;

    @Autowired
    EntrustAccountService entrustAccountService;

    @Autowired
    private PageService pageService;

    @Autowired
    private TagForBuildnameService tagForBuildnameService;

    @Autowired
    private AdCampNameCustomService adCampNameCustomService;

    @Reference
    private AudienceManagerService audienceManagerService;


    /**
     * 根据用户 查询所有发布批次
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "searchAllPublishBatch")
    @ResponseBody
    public ResponseEntity<String> searchAllPublishBatch(@RequestParam(value = "param", required = true) String param, HttpServletRequest request) {
        logger.info("PublishController searchAllPublishBatch() begin.");
        ErrorType errorType = null;
        Map<String, Object> data = null;
        try {
            User user = (User) request.getSession().getAttribute("user");
            BaseVoParam baseVoParam = JSON.parseObject(param, BaseVoParam.class);

            // 查询列表
            Integer sizeNo = baseVoParam.getPSize() == null ? CommonConstants.DEFAULT_PAGE_SIZE_100 : baseVoParam
                    .getPSize();
            Integer firstResult = baseVoParam.getCPage() == null ? 0 : (baseVoParam.getCPage() - 1) * sizeNo;

            Pager<PublishBatchDetailBo> listBo = publishBatchService.findAllPublishBatchByUserId(user.getTicket(), firstResult, sizeNo);

            if (null == listBo || null == listBo.getResultList() || null == listBo.getTotalSize()) {
                data = new HashMap<>();
                data.put("publishBatchs", "");
            } else {
                //Bo转Vo
                List<PublishBatchVoDetailResult> results = publishBatchBoToVo(listBo.getResultList());

                // 计算分页结果
                long tSize = listBo.getTotalSize();
                float nrOfPages = (float) tSize / sizeNo;
                int tPage = (int) ((nrOfPages > (int) nrOfPages || nrOfPages == 0.0) ? nrOfPages + 1 : nrOfPages);
                PageResult pageResult = new PageResult(tPage, baseVoParam.getCPage(), baseVoParam.getPSize(), tSize);

                // 封装响应结果
                data = new HashMap<>();
                data.put("publishBatchs", results);
                data.put("paginator", pageResult);
            }
        } catch (Exception e) {
            errorType = ErrorType.INTERNAL_EXCEPTION;
            logger.error(e.getMessage());
            e.printStackTrace();
        }
        OutputResult outputResult = ResponseUtils.bulidOutputResult(errorType, data);
        logger.info("PublishController searchAllPublishBatch() end.");
        return ResponseUtils.returnJsonWithUTF8(JSON.toJSONString(outputResult));
    }

    /**
     * 通过Ids查询批次
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "searchPublishBatchByIds")
    @ResponseBody
    public ResponseEntity<String> searchPublishBatchByIds(@RequestParam(value = "param", required = true) String param, HttpServletRequest request) {
        logger.info("PublishController searchAllPublishBatch() begin.");
        ErrorType errorType = null;
        Map<String, Object> data = null;
        try {
            User user = (User) request.getSession().getAttribute("user");
            Long[] ids = JSON.parseObject(param, Long[].class);

            // 查询列表
            List<PublishBatchDetailBo> listBo = publishBatchService.findPublishBatchByIdsAndUserId(user.getTicket(), ids);

            if (CollectionUtils.isEmpty(listBo)) {
                data = new HashMap<>();
                data.put("publishBatchs", "");
            } else {
                //Bo转Vo
                List<PublishBatchVoDetailResult> results = publishBatchBoToVo(listBo);

                // 封装响应结果
                data = new HashMap<>();
                data.put("publishBatchs", results);
            }

        } catch (Exception e) {
            errorType = ErrorType.INTERNAL_EXCEPTION;
            logger.error("", e);
        }
        OutputResult outputResult = ResponseUtils.bulidOutputResult(errorType, data);
        logger.info("PublishController searchAllPublishBatch() end.");
        return ResponseUtils.returnJsonWithUTF8(JSON.toJSONString(outputResult));
    }

    /**
     * 发布批次 BoToVo
     */
    private List<PublishBatchVoDetailResult> publishBatchBoToVo(List<PublishBatchDetailBo> publishBatchBos) {
        List<PublishBatchVoDetailResult> results = new ArrayList<>();
        PublishBatchVoDetailResult rs;
        for (PublishBatchDetailBo bo : publishBatchBos) {
            rs = new PublishBatchVoDetailResult();
            ConversionUtils.conversion(bo, rs);
            results.add(rs);
        }
        return results;
    }

    /**
     * 查询批次详情
     * @return
     */
    @RequestMapping(value = "searchPublishBatchDetailInfoById")
    @ResponseBody
    public ResponseEntity<String> searchPublishBatchDetailInfoById(@RequestParam(value = "param", required = true) String param) {
        logger.info("PublishController searchPublishBatchDetailInfoById() begin.");
        ErrorType errorType = null;
        Map<String, Object> data = null;

        try {
            BaseVoParam baseVoParam = JSON.parseObject(param, BaseVoParam.class);
            // 查询列表
            PublishBatchDetailInfoResult result = publishBatchService.searchPublishBatchDetailInfoById(baseVoParam.getId());
            if (result == null) {
                data = new HashMap<>();
                data.put("publishBatchDetail", "");
            } else {
                // 封装响应结果
                data = new HashMap<>();
                data.put("publishBatchDetail", result);
            }
        } catch (Exception e) {
            errorType = ErrorType.INTERNAL_EXCEPTION;
            logger.error(e.getMessage(), e);
        }
        OutputResult outputResult = ResponseUtils.bulidOutputResult(errorType, data);
        logger.info("PublishController searchPublishBatchDetailInfoById() end.");
        return ResponseUtils.returnJsonWithUTF8(JSON.toJSONString(outputResult));
    }


    /**
     * 新发布广告
     * Add by Evan 2015-08-27
     * @param param
     * @param request
     * @return
     */
    @RequestMapping(value = "newPublish")
    @ResponseBody
    public ResponseEntity<String> newPublish(@RequestParam(value = "param", required = true) String param, HttpServletRequest request) {
        logger.info("PublishController publish() begin.");
        ErrorType errorType;
        String data = null;
        PublishCacheParam publishCacheParam;
        User user = (User) request.getSession().getAttribute("user");
        String timeZone = user.getTimezoneId();
        String serverDate = DateFormatUtil.getDateModel(new Date(), "yyyyMMdd", timeZone).getToTimeString();


        try {
            publishCacheParam = JSON.parseObject(param, PublishCacheParam.class);
            PublishCache cache = cacheService.fetch(publishCacheParam.getPublishCacheId());
            /**
             * param传值校验
             */
            errorType = validatePublishParam(publishCacheParam, user, serverDate);
            if(errorType == null)
            {
                //3、设置创意
                setCreativeParamCache(cache, publishCacheParam.getCreativeParam());
                adCampNameCustomService.saveInputAdTitleHistory(user.getOrgId(), cache.getConnectionObject(), publishCacheParam.getCreativeParam().getAdTitle()); //0.7.4 在广告创意‘下一步’先保存输入标题 之前输输入过的不会保存
                //4、设置用户定位
                // 前端传来的Json转为targetingSpecGroup
                List<TargetingSpecGroupBo> groups = publishCacheParam.getUserAudienceParam();

                //设置地区分组数据
                Set<Long> customAudienceAccountIds = buildGeoLocationCache(cache);
                cache.setSupportCustomAudienceAccounts(customAudienceAccountIds);

                //设置有账号发送自定义受众
                setAccoutCustomAudiencs(customAudienceAccountIds, cache.getEntrustAccounts());

                //5、设置出价信息
                BidCache bidCache = buildBidCache(cache, publishCacheParam.getBudgetBidParam());
                cache.setBidCache(bidCache);
                cache.setSchedule(handleSchedule(publishCacheParam.getBudgetBidParam().getSchedule(), timeZone));  //设置排程
                cache.setGeoSubPublishBatchs(bulidGeoSubPublishCache(groups, bidCache));  //根据地区出价类型设置子发布批次(每组地区构成一个批次)

                //6、设置最后一步 选的广告账号、命名啥的
                tagForBuildnameService.saveSeq(user.getTicket(), cache.getConnectionObject().getFbConnectionObjectId(), cache.getProductConfiguration().getId(), publishCacheParam.getPublishParam());    //保存输入名称记录
                cache.setRatio(publishCacheParam.getPublishParam().getRatio());
                cache.setAdCampSeq(JSON.parseArray(publishCacheParam.getPublishParam().getAdCampSeq(), NameAssembly.class));
                cache.setAdSetSeq(JSON.parseArray(publishCacheParam.getPublishParam().getAdSetSeq(), NameAssembly.class));
                cache.setAdGroupSeq(JSON.parseArray(publishCacheParam.getPublishParam().getAdGroupSeq(), NameAssembly.class));
                cache.setPublishBatchName(publishCacheParam.getPublishParam().getPublishBatchName());
                cache.setAccountsInfo(publishCacheParam.getPublishParam().getAccountsInfo());
                cache.setCreateTime(new Date());

                try {
                    data = publishCacheService.publish(cache);
                    if (null == data) {
                        errorType = ErrorType.PUBLISHING_ADVERTISING_FAIL;
                    }
                }catch (AdsetLimitException e) {
                    errorType = ErrorType.ADSET_LIMIT_ERROR;
                    logger.error(e.getMessage(), e);
                } catch (Exception e) {
                    errorType = ErrorType.BUSINESS_EXCEPTION;
                    logger.error(e.getMessage(), e);
                }
            }
        } catch (BusinessException e) {
            errorType = ErrorType.BUSINESS_EXCEPTION;
            logger.error(e.getMessage(), e);
        } catch (Exception e) {
            errorType = ErrorType.INTERNAL_EXCEPTION;
            logger.error(e.getMessage(), e);
        }
        OutputResult outputResult = ResponseUtils.bulidOutputResult(errorType, data);
        logger.info("PublishController publish() end.");
        return ResponseUtils.returnJsonWithUTF8(JSON.toJSONString(outputResult));
    }


    /**
     * 电商广告发布
     * @param paramStr
     * @param req
     * @return
     * @throws Exception
     */
    @RequestMapping("ecPublish")
    @ResponseBody
    public Long ecPublish(@RequestParam("param") String paramStr, HttpServletRequest req)throws Exception{
        logger.info("ecpublish param >>" + paramStr);
        EcPublishBatchVoParam param = JSON.parseObject(paramStr,EcPublishBatchVoParam.class);
        //广告发布必要入参校验
        User user = (User) req.getSession().getAttribute("user");
        validateEcPublishParamFail(param);
        param.setAutoCreate(false);
        return publishService.publish(param, user);
    }

    //电商广告发布入参校验
    private boolean validateEcPublishParamFail(EcPublishBatchVoParam param) throws CheckedException {
        boolean flag = false;
        if(param.getEcTemplateMaterielId() == null ){
            throw new CheckedException("Error Msg:TemplateMaterielId is empty.");
        }
        if(param.getFbAccountId() == null ){
            throw new CheckedException("Error Msg:fb account is empty.");
        }
        if(param.getProductId() == null ){
            throw new CheckedException("Error Msg:product id is empty.");
        }
        if(param.getProductSetId() == null ){
            throw new CheckedException("Error Msg:product set is empty.");
        }
        if(param.getUserAudienceId() == null ){
            throw new CheckedException("Error Msg:userAudience id is empty.");
        }
        if(param.getWeFeedId() == null ){
            throw new CheckedException("Error Msg:Feed id is empty.");
        }
        if(param.getCreative().getPageId() == null ){
            throw new CheckedException("Error Msg:Page id is empty.");
        }
        if(param.getCreative().getAdTitle() == null ){
            throw new CheckedException("Error Msg:ad title is empty.");
        }
        if(param.getCreative().getAdWord() == null ){
            throw new CheckedException("Error Msg:adword is empty.");
        }

        BidInfoVoParam bidInfo = param.getBidInfo();

        if(bidInfo.getIsAutoBid()==false&&param.getBidInfo().getBidAmount()==null){
            throw new CheckedException("Error Msg:manual bid,bid amount is empty.");
        }
        if(bidInfo.getBudgetType()==null ){
            throw new CheckedException("Error Msg:budget type is empty.");
        }
        if(bidInfo.getBudgetType()==1&&(param.getBidInfo().getLifetimeBudget()==null||param.getBidInfo().getLifetimeBudget()==0) ){
            throw new CheckedException("Error Msg:budget type is lifet ,life budget is 0.");
        }

        if(bidInfo.getBudgetType()==0&&(param.getBidInfo().getDailyBudget()==null||param.getBidInfo().getDailyBudget()==0) ){
            throw new CheckedException("Error Msg:budget type is daily ,daily budget is 0.");
        }
        if(bidInfo.getStartTime()==null){
            throw new CheckedException("Error Msg:schedule start is empty.");
        }
        //logic check bid
        if(bidInfo.getIsAutoBid()==false&&bidInfo.getBidAmount()<1){
            throw new CheckedException("Error Msg:bid amount min $0.01.");
        }
        if(bidInfo.getIsAutoBid()==false&&bidInfo.getBidAmount()>1000*100){
            throw new CheckedException("Error Msg:bid amount max $1000.");
        }

//        if(bidInfo.getBudgetType()==0){ //日预算
//            if(bidInfo.getBillingEvent().equals(BillingEvent.LINK_CLICKS.toString())){
//                if(bidInfo.getDailyBudget()<500||(bidInfo.getIsAutoBid()==false&&bidInfo.getDailyBudget()<(bidInfo.getBidAmount()*5))){
//                    throw new CheckedException("Error Msg:the billing_event is LINK_CLICKS ,daily budget min $5 or bidAmount*5");
//                }
//            }
//            if(bidInfo.getBillingEvent().equals(BillingEvent.IMPRESSIONS.toString())){
//                if(bidInfo.getDailyBudget()<100||((bidInfo.getIsAutoBid()==false&&bidInfo.getDailyBudget()<bidInfo.getBidAmount()*1))){
//                    throw new CheckedException("Error Msg:the billing_event is IMPRESSIONS ,daily budget min $1 or bidAmount*1");
//                }
//            }
//        }
//        if(bidInfo.getBudgetType()==1){ // 总预算
//            SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm");
//            Date startDate = DateFormatUtil.parseDate(bidInfo.getStartTime(), format);
//            Date endDate = DateFormatUtil.parseDate(bidInfo.getEndTime(), format);
//            long diff = DateUtil.dayDiff(startDate, endDate);
//            if(bidInfo.getBillingEvent().equals(BillingEvent.LINK_CLICKS.toString())){
//                if(bidInfo.getLifetimeBudget()<500||(bidInfo.getIsAutoBid()==false&&bidInfo.getLifetimeBudget()<(bidInfo.getBidAmount()*5*diff))){
//                    throw new CheckedException("Error Msg:the billing_event is LINK_CLICKS ,daily budget min $5 or bidAmount*5*schedule");
//                }
//            }
//            if(bidInfo.getBillingEvent().equals(BillingEvent.IMPRESSIONS.toString())){
//                if(bidInfo.getLifetimeBudget()<100||(bidInfo.getIsAutoBid()==false&&bidInfo.getLifetimeBudget()<(bidInfo.getBidAmount()*1*diff))){
//                    throw new CheckedException("Error Msg:the billing_event is IMPRESSIONS ,daily budget min $1 or bidAmount*1*schedule");
//                }
//            }
//        }
        //check url
        checkUrl(param);
        return flag;
    }

    private void checkUrl(EcPublishBatchVoParam param) throws CheckedException {
        try {
            CloseableHttpClient httpClient= HttpClientBuilder.create().build();
            HttpGet httpget = new HttpGet(param.getCreative().getLink());
            CloseableHttpResponse response = httpClient.execute(httpget);
            int statusCode = response.getStatusLine().getStatusCode();
            if(statusCode!=200)
                throw new CheckedException("Error Msg: link "+param.getCreative().getLink()+",invalid");
        } catch (Exception e1) {
            throw new CheckedException("Error Msg: link "+param.getCreative().getLink()+",invalid");
        }
    }

    //新发布广告校验发布参数
    private ErrorType validatePublishParam(PublishCacheParam publishCacheParam, User user, String serverDate) throws BusinessException
    {
        //a.判断选择的创意类型
        if(StringUtil.isEmpty(publishCacheParam.getCreativeParam().getChooseType()))
        {
            return ErrorType.CREATE_CREATIVE_TYPE_FAIL;
        }
        List<String> adwords = publishCacheParam.getCreativeParam().getAdWords();
        List<AdImageVoParam> adImageList = publishCacheParam.getCreativeParam().getAdImages();
        List<AdVideoVoParam> adVideoList = publishCacheParam.getCreativeParam().getAdVideos();
        List<List<AdImageVoParam>> adImagesGroup = publishCacheParam.getCreativeParam().getAdImagesGroup();
        if (CollectionUtils.isEmpty(adwords) && CollectionUtils.isEmpty(adImageList) && CollectionUtils.isEmpty(adVideoList) && CollectionUtils.isEmpty(adImagesGroup)) {
            throw new BusinessException(ErrorCode.ERROR_60018);
        }
        //b.判断传入的主页
        Page page = pageService.getPageById(publishCacheParam.getCreativeParam().getPageId());
        if(page == null){
            return ErrorType.PARAM_EXCEPTION;
        }
        //c.判断批次名称重复    只用判断批次的前缀，不用检查多批次的原因
        String tempBatchName = serverDate + user.getNickname() + publishCacheParam.getPublishParam().getPublishBatchName();
        List<PublishBatch> publishBatchList = publishBatchService.searchPublishBatchByNameAndUserId(tempBatchName, user.getTicket());
        if (null != publishBatchList && publishBatchList.size() > 0) {
            return ErrorType.THIS_BATCH_ALREADY_EXISTS;
        }
        //d.判断广告组：广告 配比
        if(publishCacheParam.getPublishParam().getRatio() < 0 || publishCacheParam.getPublishParam().getRatio() > 50){
            return ErrorType.PARAM_EXCEPTION;
        }
        //e.判断选择的广告账号
        if(StringUtil.isEmpty(publishCacheParam.getPublishParam().getAccountsInfo())){
            return ErrorType.PARAM_EXCEPTION;
        }

        return null;
    }


    //set设置创意   PublishCache
    private void setCreativeParamCache(PublishCache cache, CacheCreativeVoParam param)
    {
        Page page = pageService.getPageById(param.getPageId());
        PageVo pageVo = new PageVo();
        ConversionUtils.conversion(page, pageVo);
        cache.setPage(pageVo);
        cache.setAdTitle(param.getAdTitle());
        cache.setAdWords(param.getAdWords());
        cache.setAdImages(param.getAdImages());
        cache.setAdVideos(param.getAdVideos());
        cache.setAdImagesGroup(param.getAdImagesGroup());
        cache.setCallToActionType(param.getCallToActionType());
        cache.setWAdCaption(param.getWAdCaption());
        cache.setWAdDesc(param.getWAdDesc());
        cache.setWAdLink(param.getWAdLink());
        cache.setWAdTagSeq(JSON.parseArray(param.getWAdTagSeq(), NameAssembly.class));
        ChooseType chooseType = ChooseType.getChooseType(param.getChooseType());
        cache.setChooseType(chooseType);
    }

    private BidCache buildBidCache(PublishCache cache, CacheBudgetBidVoParam cacheBidVoParam) {
        BidCache bidCache = cache.getBidCache();
        int geoBidType = cacheBidVoParam.getGeoBidType();
        bidCache.setGeoBidType(geoBidType);
        bidCache.setGeoBidInfos(cacheBidVoParam.getGeoBidInfos());

        //构造预览时，需要的地区信息
        Map<Long, LocalLocation> geoLocationCache = bidCache.getGeoLocationCache();
        Map<Long, LocalLocation> previewGeoLocationCache;
        if (0 == geoBidType) {
            //单地区出价 则将多组地区合并
            previewGeoLocationCache = new HashMap<>();
            LocalLocation localLocation = new LocalLocation();
            Set<Country> countries = new HashSet<>();
            Set<City> cities = new HashSet<>();
            for (LocalLocation temp : geoLocationCache.values()) {
                if (!CollectionUtils.isEmpty(temp.getCountries())) {
                    countries.addAll(temp.getCountries());
                }

                if (!CollectionUtils.isEmpty(temp.getCities())) {
                    cities.addAll(temp.getCities());
                }
            }
            localLocation.setCountries(new ArrayList<>(countries));
            localLocation.setCities(new ArrayList<>(cities));
            localLocation.setIndex(0L);
            previewGeoLocationCache.put(0L, localLocation);
        } else {
            previewGeoLocationCache = geoLocationCache;
        }
        bidCache.setPreviewGeoLocationCache(previewGeoLocationCache);
        return bidCache;
    }

    private void setAccoutCustomAudiencs(Set<Long> customAudienceAccountIds, List<EntrustAccountVo> accounts) {
        if (ListUtil.isEmpty(accounts)) {
            return;
        }

        for (EntrustAccountVo entrustAccountVo : accounts) {
            if (CollectionUtils.isEmpty(customAudienceAccountIds)
                    || customAudienceAccountIds.contains(entrustAccountVo.getFbAccountId())) {
                entrustAccountVo.setSupportCustomAudience(true);
            } else {
                entrustAccountVo.setSupportCustomAudience(false);
            }
        }
    }

    private Set<Long> buildGeoLocationCache(PublishCache publishCache) {
        UserAudienceCache userAudienceCache = publishCache.getUserAudience();
        Set<Long> accountIds = new HashSet<>();
        Map<Long, LocalLocation> geoLocationCache = new HashMap<>();
        for (TargetingSpecGroupBo publishingTargetingSpecGroup : userAudienceCache.getPublishingTargetingSpecGroups()) {
            if (null != publishingTargetingSpecGroup.getFbAccountId()) {
                //设置了自定义受众，则广告发布时，只能向自定义受众对应的账号发送
                accountIds.add(publishingTargetingSpecGroup.getFbAccountId());
            }

            LocalTargetingSpecs localTargetingSpecs = publishingTargetingSpecGroup.getLocalTargetingSpecs();
            LocalLocation targetingSpecLocalLocation = localTargetingSpecs.getLocalLocation();
            Long index = targetingSpecLocalLocation.getIndex();
            LocalLocation localLocation = geoLocationCache.get(index);
            if (null == localLocation) {
                geoLocationCache.put(index, targetingSpecLocalLocation);
            }
        }

        BidCache bidCache;
        if (null == publishCache.getBidCache()) {
            bidCache = new BidCache();
            publishCache.setBidCache(bidCache);
            //根据用户定位中的地区组数，设置默认出价方式
            if (1 < geoLocationCache.size()) {
                //多组则默认设置 多地区出价
                bidCache.setGeoBidType(1);
            } else {
                //1组则默认设置 单地区出价
                bidCache.setGeoBidType(0);
            }
        } else {
            bidCache = publishCache.getBidCache();
        }

        bidCache.setGeoLocationCache(geoLocationCache);
        return accountIds;
    }

    /**
     * 处理出价阶段->排程数据
     * @param schedule
     * @param timeZone
     * @return
     */
    private Schedule handleSchedule(Schedule schedule, String timeZone) {
        if (null == schedule) {
            schedule = new Schedule();
        }

        //如果没有设置 排程开始时间 则以当前时间为开始时间
        DateModel startTimeDateModel;
        if (StringUtil.isEmpty(schedule.getStartTime())) {
            startTimeDateModel = DateFormatUtil.getDateModel(new Date(), CommonConstants.DATE_FORMAT_YYYYMMDDHHMMSS, timeZone);
        } else {
            startTimeDateModel = DateFormatUtil.getDateModel(schedule.getStartTime(), CommonConstants.DATE_FORMAT_YYYYMMDDHHMMSS, timeZone);
        }
        schedule.setStartUTCTime(startTimeDateModel.getUtcTime());
        schedule.setStartTimeDate(startTimeDateModel.getDate());
        schedule.setStartTime(startTimeDateModel.getTimeString());
        DateModel endTimeDateModel;
        if (StringUtil.isNotEmpty(schedule.getEndTime())) {
            endTimeDateModel = DateFormatUtil.getDateModel(schedule.getEndTime(), CommonConstants.DATE_FORMAT_YYYYMMDDHHMMSS, timeZone);
            schedule.setEndUTCTime(endTimeDateModel.getUtcTime());
            schedule.setEndTimeDate(endTimeDateModel.getDate());
            schedule.setEndTime(endTimeDateModel.getTimeString());
        } else {
            schedule.setEndTimeDate(null);
            schedule.setEndUTCTime(null);
            schedule.setEndTime(null);
        }
        return schedule;
    }

    /**
     * 处理出价阶段->多地区出价
     * @param publishTargetingSpecGroups
     * @param bidCache
     * @return
     */
    private List<GeoSubPublishBatchCache> bulidGeoSubPublishCache(List<TargetingSpecGroupBo> publishTargetingSpecGroups, BidCache bidCache) {
        List<GeoSubPublishBatchCache> geoSubPublishBatchs = new ArrayList<>();
        Integer geoBidType = bidCache.getGeoBidType();
        if (0 == geoBidType) {
            //如果是单地区出价 那么只有一个子发布批次
            GeoSubPublishBatchCache geoSubPublishCache = new GeoSubPublishBatchCache();
            Long geoIndex = 0L;
            geoSubPublishCache.setIndex(geoIndex);
            geoSubPublishCache.setTargetingSpecGroups(publishTargetingSpecGroups);
            geoSubPublishCache.setBidInfos(bidCache.getGeoBidInfos().get(geoIndex));
            geoSubPublishBatchs.add(geoSubPublishCache);

        } else {
            //如果是多地区出价 那么是多个子发布批次
            Map<Long, GeoSubPublishBatchCache> geoSubPublishCacheMap = new HashMap<>();
            Long geoIndex;
            GeoSubPublishBatchCache geoSubPublishCache;
            Map<Long, List<BidInfoVoParam>> geoBidInfos = bidCache.getGeoBidInfos();
            List<TargetingSpecGroupBo> subTargetingSpecGroups;
            for (TargetingSpecGroupBo targetingSpecGroup : publishTargetingSpecGroups) {
                geoIndex = targetingSpecGroup.getLocalTargetingSpecs().getLocalLocation().getIndex();
                geoSubPublishCache = geoSubPublishCacheMap.get(geoIndex);
                if (null == geoSubPublishCache) {
                    geoSubPublishCache = new GeoSubPublishBatchCache();
                    geoSubPublishCache.setIndex(geoIndex);
                    geoSubPublishCache.setBidInfos(geoBidInfos.get(geoIndex));
                    subTargetingSpecGroups = new ArrayList<>();
                    geoSubPublishCache.setTargetingSpecGroups(subTargetingSpecGroups);
                    geoSubPublishCacheMap.put(geoIndex, geoSubPublishCache);
                } else {
                    subTargetingSpecGroups = geoSubPublishCache.getTargetingSpecGroups();
                }
                subTargetingSpecGroups.add(targetingSpecGroup);
            }

            geoSubPublishBatchs.addAll(geoSubPublishCacheMap.values());
        }
        return geoSubPublishBatchs;
    }

    /**
     * 失败的批次 发送重试事件
     * @Author Evan
     * @date   2015-07-09 11:51 AM
     * @param param
     * @param request
     * @return
     */
    @RequestMapping(value = "retry")
    @ResponseBody
    public ResponseEntity<String> retry(@RequestParam(value = "param", required = true) String param, HttpServletRequest request) {
        logger.info("PublishBatchController retry() begin.");
        ErrorType errorType = null;
        Long batchId = JSON.parseObject(param, Long.class);
        User user = (User) request.getSession().getAttribute("user");
        AdPublishCache data = null;
        if(batchId != 0L)
        {
            String key = "retrying_" + user.getTicket();
            if (redisClient.hasKey(key)) {
                errorType = ErrorType.PUBLISH_CONDITION_FLAG;
            } else {
                try {
                    redisClient.set(key, 1);
                    publishBatchService.retry(batchId, user.getTicket());
                } catch (Exception e) {
                    errorType = ErrorType.INTERNAL_EXCEPTION;
                    logger.error(e.getMessage(), e);
                } finally {
                    redisClient.delete(key);
                }
            }
        }
        OutputResult outputResult = ResponseUtils.bulidOutputResult(errorType, data);
        logger.info("PublishBatchController retry() end.");
        return ResponseUtils.returnJsonWithUTF8(JSON.toJSONString(outputResult));
    }



}
